import fs from 'fs-extra';
import path from 'path';
import * as compiler from '@vue/compiler-sfc';
import { BaseComponentParser } from '../core/BaseComponentParser';
import { VueComponent, ComponentProp, ComponentEvent, ComponentSlot, ComponentMethod, ComponentEventArgument, ComponentMethodParam, ComponentSlotBinding, ComponentMethodReturn } from '../../core/types';
import { FileUtils } from '../utils/FileUtils';
import { CommentUtils } from '../utils/CommentUtils';
import { TypeUtils } from '../utils/TypeUtils';

/**
 * Vue3组件解析器
 */
export class Vue3ComponentParser extends BaseComponentParser {
  /**
   * 构造函数
   * @param scanPaths 要扫描的组件路径
   */
  constructor(scanPaths: string[]) {
    super(scanPaths);
    console.log('初始化Vue3组件解析器');
  }

  /**
   * 解析Vue3组件
   * @param filePath 组件文件路径
   * @returns 组件信息对象
   */
  protected async parseComponent(filePath: string): Promise<VueComponent | null> {
    try {
      // 读取文件内容
      const content = await FileUtils.readFile(filePath);
      if (!content) {
        console.error(`无法读取文件: ${filePath}`);
        return null;
      }

      // 使用Vue编译器解析SFC
      const { descriptor, errors } = compiler.parse(content, { filename: filePath });
      
      if (errors && errors.length > 0) {
        console.error(`解析Vue组件 ${filePath} 时出现错误:`, errors);
        return null;
      }
      
      // 获取文件状态以获取修改时间
      const stats = await FileUtils.getFileStats(filePath);
      if (!stats) {
        console.error(`无法获取文件状态: ${filePath}`);
        return null;
      }
      
      // 获取相对路径
      const relativeFilePath = FileUtils.getRelativePath(filePath);
      
      // 提取组件信息
      const scriptContent = descriptor.script?.content || descriptor.scriptSetup?.content || '';
      const scriptLang = descriptor.script?.lang || descriptor.scriptSetup?.lang || 'js';
      
      // 解析组件名称和描述
      const componentName = await this.extractComponentName(descriptor, filePath);
      const description = this.extractComponentDescription(descriptor);
      
      // 构建组件对象
      const component: VueComponent = {
        id: this.generateComponentId(relativeFilePath),
        name: componentName,
        description: description,
        filePath: relativeFilePath,
        props: await this.extractProps(descriptor, filePath),
        events: await this.extractEvents(descriptor, filePath),
        slots: await this.extractSlots(descriptor, filePath),
        methods: await this.extractMethods(descriptor, filePath),
        createdAt: stats.birthtime.toISOString(),
        updatedAt: stats.mtime.toISOString(),
        vueVersion: '3',
      };
      
      return component;
    } catch (error) {
      console.error(`解析组件 ${filePath} 失败:`, error);
      return null;
    }
  }
  
  /**
   * 提取组件名称
   * @param descriptor Vue SFC描述符
   * @param filePath 文件路径
   * @returns 组件名称
   */
  private async extractComponentName(descriptor: compiler.SFCDescriptor, filePath: string): Promise<string> {
    // 默认使用文件名作为组件名
    let componentName = path.basename(filePath, '.vue');
    
    const scriptContent = descriptor.script?.content || descriptor.scriptSetup?.content || '';
    
    // 查找defineOptions中的name
    const defineOptionsMatch = scriptContent.match(/defineOptions\s*\(\s*\{[^}]*name\s*:\s*['"]([^'"]+)['"]/);
    if (defineOptionsMatch && defineOptionsMatch[1]) {
      return defineOptionsMatch[1];
    }
    
    // 查找直接导出的name属性
    const nameExportMatch = scriptContent.match(/export\s+default\s*\{[^}]*name\s*:\s*['"]([^'"]+)['"]/s);
    if (nameExportMatch && nameExportMatch[1]) {
      return nameExportMatch[1];
    }
    
    // 从脚本注释中查找组件名
    if (descriptor.script?.content) {
      const scriptComments = this.extractComments(descriptor.script.content);
      for (const comment of scriptComments) {
        const nameFromComment = CommentUtils.extractComponentName(comment);
        if (nameFromComment) {
          return nameFromComment;
        }
      }
    }
    
    // 查找template中的顶级注释
    if (descriptor.template?.content) {
      const templateComments = this.extractComments(descriptor.template.content);
      for (const comment of templateComments) {
        const nameFromComment = CommentUtils.extractComponentName(comment);
        if (nameFromComment) {
          return nameFromComment;
        }
      }
    }
    
    return componentName;
  }
  
  /**
   * 提取组件描述
   * @param descriptor Vue SFC描述符
   * @returns 组件描述
   */
  private extractComponentDescription(descriptor: compiler.SFCDescriptor): string {
    // 从脚本注释中查找组件描述
    if (descriptor.script?.content) {
      const scriptComments = this.extractComments(descriptor.script.content);
      if (scriptComments.length > 0) {
        // 使用第一个块注释作为组件描述
        return CommentUtils.extractDescription(scriptComments[0]);
      }
    }
    
    // 从脚本setup注释中查找组件描述
    if (descriptor.scriptSetup?.content) {
      const setupComments = this.extractComments(descriptor.scriptSetup.content);
      if (setupComments.length > 0) {
        // 使用第一个块注释作为组件描述
        return CommentUtils.extractDescription(setupComments[0]);
      }
    }
    
    // 查找template中的顶级注释
    if (descriptor.template?.content) {
      const templateComments = this.extractComments(descriptor.template.content);
      if (templateComments.length > 0) {
        return CommentUtils.extractDescription(templateComments[0]);
      }
    }
    
    return '';
  }
  
  /**
   * 提取组件属性
   * @param descriptor Vue SFC描述符
   * @param filePath 文件路径
   * @returns 组件属性数组
   */
  private async extractProps(descriptor: compiler.SFCDescriptor, filePath: string): Promise<ComponentProp[]> {
    const props: ComponentProp[] = [];
    const scriptContent = descriptor.script?.content || descriptor.scriptSetup?.content || '';
    
    // 检查withDefaults和defineProps组合
    const withDefaultsMatch = scriptContent.match(/withDefaults\s*\(\s*defineProps\s*<([^>]+)>\(\)\s*,\s*(\{[^}]+\})\s*\)/s);
    if (withDefaultsMatch) {
      const propsTypeContent = withDefaultsMatch[1];
      const defaultsContent = withDefaultsMatch[2];
      
      // 解析属性类型
      const propMatches = [...propsTypeContent.matchAll(/(\w+)\s*(\?)?:\s*([^;,]+)/g)];
      
      for (const match of propMatches) {
        const propName = match[1];
        const isOptional = match[2] === '?';
        const propType = match[3].trim();
        
        // 查找默认值
        const defaultMatch = new RegExp(`${propName}\\s*:\\s*([^,}]+)`, 'g').exec(defaultsContent);
        const defaultValue = defaultMatch ? defaultMatch[1].trim() : undefined;
        
        // 从类型中提取可能的枚举值
        const values = TypeUtils.extractEnumValuesFromTypeString(propType);
        
        props.push({
          name: propName,
          type: propType,
          description: this.findPropDescription(propName, scriptContent),
          defaultValue: defaultValue,
          required: !isOptional && !defaultValue,
          values: values,
        });
      }
      
      return props;
    }
    
    // 检查defineProps<Type>()语法
    const definePropsTypeMatch = scriptContent.match(/defineProps\s*<([^>]+)>\s*\(\)/s);
    if (definePropsTypeMatch) {
      const propsTypeContent = definePropsTypeMatch[1];
      
      // 解析属性类型
      const propMatches = [...propsTypeContent.matchAll(/(\w+)\s*(\?)?:\s*([^;,]+)/g)];
      
      for (const match of propMatches) {
        const propName = match[1];
        const isOptional = match[2] === '?';
        const propType = match[3].trim();
        
        // 从类型中提取可能的枚举值
        const values = TypeUtils.extractEnumValuesFromTypeString(propType);
        
        props.push({
          name: propName,
          type: propType,
          description: this.findPropDescription(propName, scriptContent),
          required: !isOptional,
          values: values,
        });
      }
      
      return props;
    }
    
    // 检查defineProps({})语法
    const definePropsObjectMatch = scriptContent.match(/defineProps\s*\(\s*(\{[^}]+\})\s*\)/s);
    if (definePropsObjectMatch) {
      const propsObjectContent = definePropsObjectMatch[1];
      
      // 解析属性对象
      const propMatches = [...propsObjectContent.matchAll(/(\w+)\s*:\s*(?:\{([^}]+)\}|([^,]+))/gs)];
      
      for (const match of propMatches) {
        const propName = match[1];
        const propConfig = match[2] || match[3];
        
        if (!propConfig) continue;
        
        let type = 'any';
        let required = false;
        let defaultValue: string | undefined;
        
        // 如果是对象配置
        if (match[2]) {
          // 提取类型
          const typeMatch = propConfig.match(/type\s*:\s*([^,]+)/);
          if (typeMatch) {
            type = typeMatch[1].trim();
          }
          
          // 提取是否必填
          const requiredMatch = propConfig.match(/required\s*:\s*(true|false)/);
          if (requiredMatch) {
            required = requiredMatch[1] === 'true';
          }
          
          // 提取默认值
          const defaultMatch = propConfig.match(/default\s*:\s*(?:function\s*\(\)\s*\{\s*return\s*([^;]+);\s*\}|([^,]+))/);
          if (defaultMatch) {
            defaultValue = (defaultMatch[1] || defaultMatch[2]).trim();
          }
        } else {
          // 如果是简单类型
          type = propConfig.trim();
        }
        
        // 根据类型名称获取实际的类型字符串
        if (type.includes('String')) type = 'string';
        else if (type.includes('Number')) type = 'number';
        else if (type.includes('Boolean')) type = 'boolean';
        else if (type.includes('Array')) type = 'array';
        else if (type.includes('Object')) type = 'object';
        else if (type.includes('Function')) type = 'function';
        
        props.push({
          name: propName,
          type: type,
          description: this.findPropDescription(propName, scriptContent),
          defaultValue: defaultValue,
          required: required,
          values: [],
        });
      }
      
      return props;
    }
    
    return props;
  }
  
  /**
   * 提取组件事件
   * @param descriptor Vue SFC描述符
   * @param filePath 文件路径
   * @returns 组件事件数组
   */
  private async extractEvents(descriptor: compiler.SFCDescriptor, filePath: string): Promise<ComponentEvent[]> {
    const events: ComponentEvent[] = [];
    const scriptContent = descriptor.script?.content || descriptor.scriptSetup?.content || '';
    
    // 查找emit定义
    const defineEmitsMatch = scriptContent.match(/defineEmits\s*<\{([^}]+)\}>\s*\(\)/s);
    if (defineEmitsMatch) {
      const emitsContent = defineEmitsMatch[1];
      const emitMatches = [...emitsContent.matchAll(/\(e\s*:\s*['"]([^'"]+)['"]\s*(?:,\s*([^)]+))?\)\s*=>\s*void/g)];
      
      for (const match of emitMatches) {
        const eventName = match[1];
        const argsContent = match[2];
        const args: ComponentEventArgument[] = [];
        
        if (argsContent) {
          const argMatches = [...argsContent.matchAll(/(\w+)\s*:\s*([^,]+)/g)];
          for (const argMatch of argMatches) {
            args.push({
              name: argMatch[1],
              type: argMatch[2].trim(),
              description: '',
            });
          }
        }
        
        events.push({
          name: eventName,
          description: this.findEventDescription(eventName, scriptContent),
          arguments: args,
        });
      }
      
      return events;
    }
    
    // 查找defineEmits数组语法
    const defineEmitsArrayMatch = scriptContent.match(/defineEmits\s*\(\s*\[((?:'[^']+'\s*,?\s*|"[^"]+"\s*,?\s*)+)\]\s*\)/s);
    if (defineEmitsArrayMatch) {
      const emitsArray = defineEmitsArrayMatch[1];
      const eventMatches = [...emitsArray.matchAll(/['"]([^'"]+)['"]/g)];
      
      for (const match of eventMatches) {
        const eventName = match[1];
        
        events.push({
          name: eventName,
          description: this.findEventDescription(eventName, scriptContent),
          arguments: [],
        });
      }
      
      return events;
    }
    
    // 查找模板中的emit调用
    if (descriptor.template?.content) {
      const templateContent = descriptor.template.content;
      const emitMatches = [...templateContent.matchAll(/\$emit\s*\(\s*['"]([^'"]+)['"]/g)];
      
      for (const match of emitMatches) {
        const eventName = match[1];
        
        // 检查事件是否已存在
        const existingEvent = events.find(e => e.name === eventName);
        if (!existingEvent) {
          events.push({
            name: eventName,
            description: this.findEventDescription(eventName, scriptContent),
            arguments: [],
          });
        }
      }
    }
    
    return events;
  }
  
  /**
   * 提取组件插槽
   * @param descriptor Vue SFC描述符
   * @param filePath 文件路径
   * @returns 组件插槽数组
   */
  private async extractSlots(descriptor: compiler.SFCDescriptor, filePath: string): Promise<ComponentSlot[]> {
    const slots: ComponentSlot[] = [];
    
    // 解析模板中的插槽定义
    if (descriptor.template?.content) {
      const templateContent = descriptor.template.content;
      
      // 查找命名插槽
      const namedSlotMatches = [...templateContent.matchAll(/<slot\s+name=(['"])([^'"]+)\1[^>]*>/g)];
      
      for (const match of namedSlotMatches) {
        const slotName = match[2];
        const slotTag = match[0];
        
        // 检查插槽是否已存在
        const existingSlot = slots.find(s => s.name === slotName);
        if (!existingSlot) {
          // 查找slot上的属性，它们可能是绑定变量
          const bindingMatches = [...slotTag.matchAll(/\s+:([^=]+)=(['"])([^'"]+)\2/g)];
          const bindings: ComponentSlotBinding[] = [];
          
          for (const bindingMatch of bindingMatches) {
            bindings.push({
              name: bindingMatch[1],
              type: 'any', // 默认类型
              description: '',
            });
          }
          
          slots.push({
            name: slotName,
            description: this.findSlotDescription(slotName, descriptor),
            bindings: bindings,
          });
        }
      }
      
      // 查找默认插槽
      const defaultSlotMatch = templateContent.match(/<slot\s+[^>]*>/);
      if (defaultSlotMatch) {
        const slotTag = defaultSlotMatch[0];
        
        // 检查默认插槽是否已存在
        const existingDefaultSlot = slots.find(s => s.name === 'default');
        if (!existingDefaultSlot) {
          // 查找slot上的属性，它们可能是绑定变量
          const bindingMatches = [...slotTag.matchAll(/\s+:([^=]+)=(['"])([^'"]+)\2/g)];
          const bindings: ComponentSlotBinding[] = [];
          
          for (const bindingMatch of bindingMatches) {
            bindings.push({
              name: bindingMatch[1],
              type: 'any',
              description: '',
            });
          }
          
          slots.push({
            name: 'default',
            description: this.findSlotDescription('default', descriptor),
            bindings: bindings,
          });
        }
      }
    }
    
    return slots;
  }
  
  /**
   * 提取组件方法
   * @param descriptor Vue SFC描述符
   * @param filePath 文件路径
   * @returns 组件方法数组
   */
  private async extractMethods(descriptor: compiler.SFCDescriptor, filePath: string): Promise<ComponentMethod[]> {
    const methods: ComponentMethod[] = [];
    const scriptContent = descriptor.script?.content || descriptor.scriptSetup?.content || '';
    
    // 查找defineExpose调用
    const defineExposeMatch = scriptContent.match(/defineExpose\s*\(\s*(\{[^}]+\})\s*\)/s);
    if (defineExposeMatch) {
      const exposeContent = defineExposeMatch[1];
      
      // 查找已经在该对象中列出的函数
      const functionMatches = [...exposeContent.matchAll(/(\w+)(?:\s*,|\s*$)/g)];
      
      for (const match of functionMatches) {
        const methodName = match[1];
        
        // 查找该方法的实际定义
        const methodDefinitionMatch = new RegExp(`(?:function\\s+${methodName}|const\\s+${methodName}\\s*=\\s*(?:function|\\([^)]*\\)\\s*=>))\\s*\\(([^)]*)\\)\\s*(?::\\s*([^{]+))?\\s*\\{`, 'g').exec(scriptContent);
        
        if (methodDefinitionMatch) {
          const paramsString = methodDefinitionMatch[1] || '';
          const returnTypeString = methodDefinitionMatch[2] || '';
          
          // 解析参数
          const params: ComponentMethodParam[] = this.parseMethodParams(paramsString, scriptContent, methodName);
          
          // 解析返回类型
          let returnType = returnTypeString.trim();
          if (!returnType) {
            returnType = 'void';
          }
          
          const methodComment = this.findMethodComment(methodName, scriptContent);
          
          // 查找方法注释中的返回值描述
          const returnDescription = CommentUtils.extractReturnDescription(methodComment) || '';
          
          methods.push({
            name: methodName,
            description: methodComment ? CommentUtils.extractDescription(methodComment) : '',
            params: params,
            returns: {
              type: returnType,
              description: returnDescription,
            },
          });
        }
      }
    }
    
    // 查找直接定义的函数
    const functionDefMatches = [...scriptContent.matchAll(/(?:^|\n)\s*(?:export\s+)?(?:function\s+(\w+)|const\s+(\w+)\s*=\s*(?:function|(?:async)?\s*\([^)]*\)\s*=>))\s*\(([^)]*)\)\s*(?::\s*([^{]+))?/g)];
    
    for (const match of functionDefMatches) {
      const methodName = match[1] || match[2];
      const paramsString = match[3] || '';
      const returnTypeString = match[4] || '';
      
      // 跳过内部或私有方法（以下划线开头）
      if (methodName.startsWith('_')) {
        continue;
      }
      
      // 解析参数
      const params: ComponentMethodParam[] = this.parseMethodParams(paramsString, scriptContent, methodName);
      
      // 解析返回类型
      let returnType = returnTypeString.trim();
      if (!returnType) {
        returnType = 'void';
      }
      
      const methodComment = this.findMethodComment(methodName, scriptContent);
      
      // 查找方法注释中的返回值描述
      const returnDescription = CommentUtils.extractReturnDescription(methodComment) || '';
      
      methods.push({
        name: methodName,
        description: methodComment ? CommentUtils.extractDescription(methodComment) : '',
        params: params,
        returns: {
          type: returnType,
          description: returnDescription,
        },
      });
    }
    
    return methods;
  }
  
  /**
   * 解析方法参数
   * @param paramsString 参数字符串
   * @param scriptContent 脚本内容
   * @param methodName 方法名称
   * @returns 参数数组
   */
  private parseMethodParams(paramsString: string, scriptContent: string, methodName: string): ComponentMethodParam[] {
    const params: ComponentMethodParam[] = [];
    const methodComment = this.findMethodComment(methodName, scriptContent);
    
    // 从注释中提取参数信息
    const commentParams = methodComment ? CommentUtils.extractParams(methodComment) : [];
    
    // 解析参数字符串
    const paramMatches = [...paramsString.matchAll(/(\w+)(?:\s*:\s*([^,]+))?(?:\s*=\s*([^,]+))?/g)];
    
    for (const match of paramMatches) {
      const paramName = match[1];
      const paramType = match[2] ? match[2].trim() : null;
      const defaultValue = match[3] ? match[3].trim() : undefined;
      
      // 从注释中查找参数描述
      const commentParam = commentParams.find(p => p.name === paramName);
      const description = commentParam ? commentParam.description : '';
      
      // 合并参数类型（优先使用代码中的类型，如果没有则使用注释中的类型）
      const finalType = TypeUtils.mergeTypes(
        paramType,
        commentParam?.type || undefined
      );
      
      params.push({
        name: paramName,
        type: finalType,
        description: description,
        defaultValue: defaultValue,
      });
    }
    
    return params;
  }
  
  /**
   * 查找属性描述
   * @param propName 属性名
   * @param scriptContent 脚本内容
   * @returns 属性描述
   */
  private findPropDescription(propName: string, scriptContent: string): string {
    // 查找属性的注释
    const propCommentPattern = new RegExp(`\\/\\*\\*((?:[^*]|\\*[^/])*)\\*\\/\\s*(?:${propName}\\s*[?:]|${propName}\\s*\\(|${propName}:|${propName}\\s*=)`, 's');
    const commentMatch = propCommentPattern.exec(scriptContent);
    
    if (commentMatch) {
      return CommentUtils.extractDescription(commentMatch[1]);
    }
    
    return '';
  }
  
  /**
   * 查找事件描述
   * @param eventName 事件名
   * @param scriptContent 脚本内容
   * @returns 事件描述
   */
  private findEventDescription(eventName: string, scriptContent: string): string {
    // 查找事件的注释
    const eventCommentPattern = new RegExp(`\\/\\*\\*((?:[^*]|\\*[^/])*)\\*\\/\\s*.*${eventName}`, 's');
    const commentMatch = eventCommentPattern.exec(scriptContent);
    
    if (commentMatch) {
      return CommentUtils.extractDescription(commentMatch[1]);
    }
    
    return '';
  }
  
  /**
   * 查找插槽描述
   * @param slotName 插槽名
   * @param descriptor Vue SFC描述符
   * @returns 插槽描述
   */
  private findSlotDescription(slotName: string, descriptor: compiler.SFCDescriptor): string {
    if (!descriptor.template?.content) {
      return '';
    }
    
    const templateContent = descriptor.template.content;
    
    // 查找slot标签前的注释
    const slotPattern = slotName === 'default' 
      ? /(<!--((?:[^-]|(?:-(?!->)))*)--->)?\s*<slot\s+[^>]*>/
      : new RegExp(`(<!--((?:[^-]|(?:-(?!->)))*)--->)?\\s*<slot\\s+name=['"']${slotName}['"'][^>]*>`, 's');
    
    const slotMatch = slotPattern.exec(templateContent);
    
    if (slotMatch && slotMatch[1]) {
      // 提取注释内容
      const comment = slotMatch[2];
      if (comment) {
        return comment.trim();
      }
    }
    
    return '';
  }
  
  /**
   * 查找方法注释
   * @param methodName 方法名
   * @param scriptContent 脚本内容
   * @returns 方法注释
   */
  private findMethodComment(methodName: string, scriptContent: string): string | null {
    // 查找方法的注释
    const methodCommentPattern = new RegExp(`\\/\\*\\*((?:[^*]|\\*[^/])*)\\*\\/\\s*(?:function\\s+${methodName}|const\\s+${methodName}\\s*=)`, 's');
    const commentMatch = methodCommentPattern.exec(scriptContent);
    
    if (commentMatch) {
      return commentMatch[1];
    }
    
    return null;
  }
  
  /**
   * 提取注释
   * @param content 源代码内容
   * @returns 注释数组
   */
  private extractComments(content: string): string[] {
    const comments: string[] = [];
    
    // 提取块注释 /* ... */
    const blockCommentPattern = /\/\*\*((?:[^*]|\*[^/])*)\*\//g;
    const blockMatches = [...content.matchAll(blockCommentPattern)];
    
    for (const match of blockMatches) {
      comments.push(match[1]);
    }
    
    // 提取行注释 //
    const lineCommentPattern = /\/\/\s*(.*)/g;
    const lineMatches = [...content.matchAll(lineCommentPattern)];
    
    for (const match of lineMatches) {
      comments.push(match[1]);
    }
    
    return comments;
  }
} 